package ooboo.gui;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

import ooboo.Assets;
import ooboo.Authenticator;
import ooboo.ImageCache;
import ooboo.Ooboo;
import ooboo.StoredTweet;
import ooboo.TweetThread;

public class ThreadPanel extends JPanel implements MouseListener, PostListener
{
	private static final long serialVersionUID = 1L;
	private static final int MIN_WIDTH = 300;
	private static final int MIN_HEIGHT = 70;
	
	private TweetThread thread;
	private JEditorPane[] panes;
	private int[] offsets;
	private boolean[] twoLines;
	private Link[][] links;
	private PostPanel postPanel;
	
	private int userHeight;
	private int dateHeight;
	
	public ThreadPanel(TweetThread thread)
	{
		this.thread = thread;
		panes = new JEditorPane[thread.size()];
		twoLines = new boolean[thread.size()];
		links = new Link[thread.size()][];
		setLayout(null);

		Pattern mentionPattern = Pattern.compile("@[a-zA-Z0-9_]{1,16}");
		Pattern hashPattern = Pattern.compile("#[a-zA-Z0-9_]+");
		Pattern urlPattern = Pattern.compile("((^@)(www\\.)?([a-zA-Z]*\\.\\-)+(com|org|net|edu|ca|biz|gov)(/[a-zA-Z:#@%/;$()~_?\\+-=\\.&]*[a-zA-Z0-9\\/)])*)|((https?):((//)|(\\\\))+[a-zA-Z:#@%/;$()~_?\\+-=\\.&]*[a-zA-Z0-9\\/)])"); //("\\bhttps?://[a-zA-Z0-9\\.]*[a-zA-Z0-9/]*(\\.[a-zA-Z]*)?");
		//Pattern emailPattern = Pattern.compile("[a-zA-Z][a-zA-Z_\\.+\\-]*@[a-zA-Z\\.\\-]*[a-zA-z]");
		
		for(int i = 0; i < thread.size(); i++)
		{
			StoredTweet tweet = i == 0?thread.getRoot():thread.getReplies()[i - 1];
			
			JTextPane pane = new JTextPane();
			pane.addMouseListener(this);
			pane.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
			pane.setEditable(false);
			pane.setFont(i == 0?Assets.TEXT_FONT:Assets.REPLY_FONT);
			pane.setMargin(new Insets(5,5,5,5));
			pane.setOpaque(false);
			pane.setText(tweet.getText());
			
			StyledDocument doc = pane.getStyledDocument();
			Style defaultStyle = doc.getStyle(StyleContext.DEFAULT_STYLE);
			
			Style linkStyle = doc.addStyle("link",defaultStyle);
			Style hashStyle = doc.addStyle("hash",defaultStyle);
			Style mentionStyle = doc.addStyle("mention",defaultStyle);
			//Style emailStyle = doc.addStyle("email",defaultStyle);
			
			//StyleConstants.setUnderline(linkStyle,true);
			StyleConstants.setForeground(linkStyle,new Color(67,143,191));
			//StyleConstants.setForeground(emailStyle,new Color(67,143,191));
			StyleConstants.setForeground(hashStyle,Color.GRAY);
			StyleConstants.setForeground(mentionStyle,new Color(67,143,191));
			StyleConstants.setBold(mentionStyle, true);

			try
			{
				Matcher m = urlPattern.matcher(doc.getText(0,doc.getLength()));
				ArrayList<Link> tweetLinks = new ArrayList<Link>(2);
				
				while(m.find())
				{
					int start = m.start();
					int end = m.end();
					
					if (doc.getText(end-1, 1).equals(")") && doc.getText(start-1, 1).equals("(")) {
						end--;
					}
					
					doc.setCharacterAttributes(start,end - start,linkStyle,false);
					Link link = new Link(doc.getText(start,end - start),start,end);
					add(link);
					tweetLinks.add(link);
				}
				
				/*m = emailPattern.matcher(doc.getText(0,doc.getLength()));
				while(m.find())
				{
					int start = m.start();
					int end = m.end();
					
					if (doc.getText(end-1, 1).equals(")") && doc.getText(start-1, 1).equals("(")) {
						end--;
					}
					
					doc.setCharacterAttributes(start,end - start,emailStyle,false);
					Link link = new Link("mailto:"+doc.getText(start,end - start),start,end);
					add(link);
					tweetLinks.add(link);
				}*/
				
				m = mentionPattern.matcher(doc.getText(0,doc.getLength()));
				while(m.find())
				{
					String username = doc.getText(m.start()+1,m.end() - m.start() - 1);
					username = username.substring(username.indexOf("@")+1);
					doc.setCharacterAttributes(m.start(),m.end() - m.start(),mentionStyle,false);
					Link link = new Link("http://twitter.com/" + username,m.start(),m.end());
					add(link);
					tweetLinks.add(link);
				}
				
				m = hashPattern.matcher(doc.getText(0,doc.getLength()));
				while(m.find())
				{
					doc.setCharacterAttributes(m.start(),m.end() - m.start(),hashStyle,false);
					Link link = new Link("http://twitter.com/search?q=%23" + doc.getText(m.start()+1,m.end() - m.start() - 1),m.start(),m.end());
					add(link);
					tweetLinks.add(link);
				}
				
				links[i] = tweetLinks.toArray(new Link[tweetLinks.size()]);
			}
			catch(BadLocationException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			panes[i] = pane;
			add(pane);
		}
		
		addComponentListener(
				new ComponentAdapter()
				{
					public void componentResized(ComponentEvent e)
					{
						layoutThread();
					}
				});
	}

	public void mouseClicked(MouseEvent e)
	{
		if(e.getClickCount() == 2)
		{
			for(int i = 0; i < panes.length; i++)
			{
				if(e.getSource() == panes[i])
				{
					panes[i].setSelectionStart(0);
					panes[i].setSelectionEnd(0);
					
					if(postPanel != null)
						remove(postPanel);
					
					StoredTweet replyTo = i == 0?thread.getRoot():thread.getReplies()[i - 1];
					postPanel = new PostPanel(Ooboo.curScreenName,true,replyTo.getId(),replyTo.getScreenName());
					postPanel.addPostListener(this);
					add(postPanel);
					
					layoutThread();
					
					break;
				}
			}
		}
	}
	public void mouseEntered(MouseEvent e){}
	public void mouseExited(MouseEvent e){}
	public void mousePressed(MouseEvent e){}
	public void mouseReleased(MouseEvent e){}
	
	public void postSent(PostEvent e)
	{
		if(e.isReply())
			Authenticator.reply(e.getScreenName(),e.getText(),e.getReplyToStatusID());
		
		remove(postPanel);
		postPanel = null;
		layoutThread();
	}
	public void postCanceled(PostEvent e){}
	
	/*public void validate()
	{
		super.validate();
		
		layoutThread();
	}*/
	
	private void layoutThread()
	{
		offsets = new int[thread.size()];
		userHeight = getGraphics().getFontMetrics(Assets.USER_FONT).getHeight();
		dateHeight = getGraphics().getFontMetrics(Assets.DATE_FONT).getHeight();
		
		int offset = 0;
		int maxWidth = getWidth() - 84;
		twoLines[0] = getGraphics().getFontMetrics(Assets.USER_FONT).stringWidth(thread.getRoot().getScreenName()) + 
							getGraphics().getFontMetrics(Assets.DATE_FONT).stringWidth(thread.getRoot().getDateString())
							> maxWidth - 8;
		panes[0].setSize(maxWidth,Integer.MAX_VALUE);
		panes[0].setBounds(74,userHeight + (twoLines[0]?dateHeight:0) + 9,maxWidth,panes[0].getPreferredSize().height);
		offsets[0] = 0;
		offset = Math.max(MIN_HEIGHT,
				panes[0].getPreferredSize().height + userHeight + (twoLines[0]?dateHeight:0) + 20);
		
		for(Link link:links[0])
		{
			try
			{
				Rectangle startRect = panes[0].modelToView(link.getStart());
				Rectangle endRect = panes[0].modelToView(link.getEnd());
				link.setBounds(startRect.x + panes[0].getX(),startRect.y + panes[0].getY(),
						endRect.x - startRect.x,startRect.height);
			}
			catch(BadLocationException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		maxWidth -= 64;
		int i = 1;
		for(StoredTweet reply:thread.getReplies())
		{
			twoLines[i] = getGraphics().getFontMetrics(Assets.USER_FONT).stringWidth(reply.getScreenName()) + 
					getGraphics().getFontMetrics(Assets.DATE_FONT).stringWidth(reply.getDateString())
					> maxWidth - 8;
			panes[i].setSize(maxWidth,Integer.MAX_VALUE);
			panes[i].setBounds(74,offset + userHeight + (twoLines[i]?dateHeight:0) + 10,maxWidth,
					panes[i].getPreferredSize().height);
			offsets[i] = offset;
			offset += Math.max(MIN_HEIGHT,
					panes[i].getPreferredSize().height + userHeight + (twoLines[i]?dateHeight:0) + 20);
			
			for(Link link:links[i])
			{
				try
				{
					Rectangle startRect = panes[i].modelToView(link.getStart());
					Rectangle endRect = panes[i].modelToView(link.getEnd());
					link.setBounds(startRect.x + panes[0].getX(),startRect.y + panes[i].getY(),
							endRect.x - startRect.x,startRect.height);
				}
				catch(BadLocationException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			i++;
		}
		
		if(postPanel != null)
		{
			postPanel.setBounds(0,offset,getWidth(),postPanel.getPreferredSize().height);
			offset += postPanel.getPreferredSize().height;
		}
		
		setMaximumSize(new Dimension(getMaximumSize().width,offset));
		setPreferredSize(new Dimension(MIN_WIDTH,offset));
		setMinimumSize(new Dimension(MIN_WIDTH,offset));
		
		revalidate();
	}
	
	protected void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		
		Graphics2D g2 = (Graphics2D)g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		
		if(offsets == null)
			layoutThread();
		
		g2.drawImage(ImageCache.getImage(thread.getRoot().getScreenName()),4,g2.getFontMetrics(Assets.USER_FONT).getHeight()+4,48,48,this);
		
		g2.setColor(Color.BLACK);

		g2.setFont(Assets.USER_FONT);
		g2.drawString(thread.getRoot().getScreenName(),67,userHeight);

		if (thread.getRoot().getScreenName().equals(Ooboo.curScreenName)) {
			Bubble.drawBubble(Bubble.BLUE,g2,userHeight + (twoLines[0]?dateHeight:0) + 3,getWidth() - 2,
					userHeight + (twoLines[0]?dateHeight:0) + panes[0].getHeight() + 16,54);
		} else if (thread.getRoot().getText().contains("@" + Ooboo.curScreenName)) {
			Bubble.drawBubble(Bubble.GREEN,g2,userHeight + (twoLines[0]?dateHeight:0) + 3,getWidth() - 2,
					userHeight + (twoLines[0]?dateHeight:0) + panes[0].getHeight() + 16,54);
		} else {
			Bubble.drawBubble(Bubble.WHITE,g2,userHeight + (twoLines[0]?dateHeight:0) + 3,getWidth() - 2,
					userHeight + (twoLines[0]?dateHeight:0) + panes[0].getHeight() + 16,54);
		}

		g2.setColor(Color.BLACK);
		g2.setFont(Assets.DATE_FONT);
		g2.drawString(thread.getRoot().getDateString(),
				getWidth() - g2.getFontMetrics(Assets.DATE_FONT).stringWidth(thread.getRoot().getDateString()) - 7,
				userHeight + (twoLines[0]?dateHeight:0));

		FontMetrics fm = g2.getFontMetrics(Assets.USER_FONT);
		int i = 1;
		for(StoredTweet reply:thread.getReplies())
		{
			g2.drawImage(ImageCache.getImage(reply.getScreenName()),getWidth() - 56,
					offsets[i] + (twoLines[i]?dateHeight:0)+ g2.getFontMetrics(Assets.USER_FONT).getHeight() + 4,48,48,this);
			
			g2.setColor(Color.BLACK);
			
			g2.setFont(Assets.USER_FONT);
			g2.drawString(reply.getScreenName(),
					getWidth() - fm.stringWidth(reply.getScreenName()) - 76,
					offsets[i] + userHeight);

			if (reply.getScreenName().equals(Ooboo.curScreenName)) {
				Bubble.drawReply(Bubble.BLUE,g2,offsets[i] + userHeight + (twoLines[i]?dateHeight:0) + 3,
						getWidth() - 58,
						offsets[i] + userHeight + (twoLines[i]?dateHeight:0) + panes[i].getHeight() + 16,64);
			} else if (reply.getText().contains("@" + Ooboo.curScreenName)) {
				Bubble.drawReply(Bubble.GREEN,g2,offsets[i] + userHeight + (twoLines[i]?dateHeight:0) + 3,
						getWidth() - 58,
						offsets[i] + userHeight + (twoLines[i]?dateHeight:0) + panes[i].getHeight() + 16,64);
			} else {
				Bubble.drawReply(Bubble.WHITE,g2,offsets[i] + userHeight + (twoLines[i]?dateHeight:0) + 3,
						getWidth() - 58,
						offsets[i] + userHeight + (twoLines[i]?dateHeight:0) + panes[i].getHeight() + 16,64);
			}
			
			g2.setColor(Color.BLACK);
			g2.setFont(Assets.DATE_FONT);
			g2.drawString(reply.getDateString(),74,offsets[i] + userHeight + (twoLines[i]?dateHeight:0));
			i++;
		}
	}
}
